#include <iostream>
#include <fstream>
#include <filesystem>
#include <memory>
#include <sstream>
#include <unistd.h>
#include "mutex.hpp"
using namespace mymutex;
namespace mylog
{
    class logflush
    {
    public:
        virtual void flush(std::string massage) = 0;
        // ~logflush() = delete;
    };
    // 显示器刷新
    class displayflush : public logflush
    {
    public:
        void flush(std::string massage) override
        {
            lockgroup lgp(_mutex);
            std::cout << massage << std::endl;
        }

    private:
        Mutex _mutex;
    };
    static std::string default_path = "./log";
    static std::string default_name = "log.log";
    const std::string gsep = "\r\n";
    class fileflush : public logflush
    {
    public:
        fileflush(const std::string &path = default_path, const std::string &name = default_name)
            : _path(path), _name(name)
        {
            if (std::filesystem::exists(path))
            {
                return;
            }
            // 路径不存在，创建
            try
            {
                std::filesystem::create_directories(_path);
            }
            catch (const std::filesystem::filesystem_error &e)
            {
                std::cerr << e.what() << gsep;
            }
        }
        void flush(std::string massage) override
        {
            lockgroup lgp(_mutex);
            _pathname = _path + (_path.back() == '/' ? ' ' : '/') + _name;
            std::ofstream out(_pathname, std::ios::app);
            if (!out.is_open())
            {
                // 打开文件失败
                return;
            }
            out << massage << gsep;
        }

    private:
        std::string _path;
        std::string _name;
        std::string _pathname;
        Mutex _mutex;
    };
    std::string GetTime()
    {
        time_t tm = time(nullptr);
        struct tm curr;
        localtime_r(&tm, &curr);
        std::stringstream ss;
        ss << curr.tm_year + 1900 << "-"
           << curr.tm_mon + 1 << "-"
           << curr.tm_mday << " "
           << curr.tm_hour << ":"
           << curr.tm_min << ":"
           << curr.tm_sec;
        return ss.str();
    }
    enum class Level
    {
        DEBUG,
        INFO,
        WARNING,
        ERROR,
        FATAL
    };
    std::string GetLevel(Level level)
    {
        switch (level)
        {
        case Level::DEBUG:
            return "DEBUG";
        case Level::INFO:
            return "INFO";
        case Level::WARNING:
            return "WARNING";
        case Level::ERROR:
            return "ERROR";
        case Level::FATAL:
            return "FATAL";
        default:
            return "UNKONW";
        }
    }
    // 日志
    class Log
    {
    public:
        Log()
        {
            _logflush = std::make_unique<displayflush>();
        }
        void EnableDisplayFlush()
        {
            _logflush = std::make_unique<displayflush>();
        }
        void EnableFileFlush()
        {
            _logflush = std::make_unique<fileflush>();
        }

        class Logmassage
        {
        public:
            Logmassage(const std::string &time, Level level, const std::string &filename, int line, Log *plog)
                : _time(time), _level(level), _pid(getpid()), _filename(filename), _line(line), _log(plog)
            {
                std::stringstream ss;
                ss << '[' << _time << ']'
                   << '[' << GetLevel(level) << ']' /*枚举类型，默认是整型*/
                   << '[' << _pid << ']'
                   << '[' << _filename << ']'
                   << '[' << _line << "] : ";
                _logmassage = ss.str();
            }
            template <typename T>
            Logmassage &operator<<(const T &data)
            {
                std::stringstream ss;
                ss << data;
                _logmassage += ss.str();
                return *this;
            }
            ~Logmassage()
            {
                if (_log->_logflush)
                {
                    _log->_logflush->flush(_logmassage);
                }
            }

        private:
            std::string _time;       // 日志时间
            Level _level;            // 日志等级
            pid_t _pid;              // 进程id
            std::string _filename;   // 文件名
            int _line;               // 行号
            std::string _logmassage; // 完整的日志信息
            Log *_log;               // log指针，方便刷新日志信息
        };
        Logmassage operator()(const std::string &time, Level level, const std::string &filename, int line)
        {
            return Logmassage(time, level, filename, line, this);
        }

    private:
        std::unique_ptr<logflush> _logflush;
    };
    Log log;
#define LOG(level) log(GetTime(), level, __FILE__, __LINE__)
}

// using namespace mymutex;

// namespace mylog
// {
//     const std::string gsep = "\r\n";
//     // 刷新策略
//     class logflush
//     {
//     public:
//         virtual void flush(std::string msg) = 0;
//         // ~logflush() = delete;
//     };
//     class displayflush : public logflush
//     {
//     public:
//         void flush(std::string msg) override
//         {
//             lockgroup ldp(_mutex);
//             std::cout << msg << std::endl;
//         }

//     private:
//         Mutex _mutex;
//     };
//     static std::string default_path = "./log";
//     static std::string default_name = "log.log";
//     class fileflush : public logflush
//     {
//     public:
//         fileflush(const std::string &path = default_path, const std::string &name = default_name)
//             : _path(path), _name(name)
//         {
//             lockgroup ldp(_mutex);
//             if (std::filesystem::exists(_path))
//             {
//                 // 路径存在
//                 return;
//             }
//             // 路径不存在，创建
//             try
//             {
//                 std::filesystem::create_directories(_path);
//             }
//             catch (const std::filesystem::filesystem_error &e)
//             {
//                 std::cerr << e.what() << gsep;
//             }
//         }
//         void flush(std::string msg) override
//         {
//             lockgroup lgp(_mutex);
//             _pathname = _path + (_path.back() == '/' ? "" : "/") + _name; // 生成文件路径+名称
//             std::ofstream out(_pathname, std::ios::app);                  // 以追加方式打开文件
//             if (!out.is_open())
//             {
//                 // 打开文件失败
//                 return;
//             }
//             // 输出
//             out << msg << gsep;
//             out.close();
//         }

//     private:
//         std::string _path;
//         std::string _name;
//         std::string _pathname;
//         Mutex _mutex;
//     };
//     std::string GetTime()
//     {
//         time_t tm = time(nullptr);
//         struct tm curr;
//         localtime_r(&tm, &curr);
//         std::stringstream ss;
//         ss << curr.tm_year + 1900 << "-"
//            << curr.tm_mon + 1 << "-"
//            << curr.tm_mday << " "
//            << curr.tm_hour << ":"
//            << curr.tm_min << ":"
//            << curr.tm_sec;
//         return ss.str();
//     }
//     enum class Level
//     {
//         DEBUG,
//         INFO,
//         WARNING,
//         ERROR,
//         FATAL
//     };
//     std::string GetLevel(Level level)
//     {
//         switch (level)
//         {
//         case Level::DEBUG:
//             return "DEBUG";
//         case Level::INFO:
//             return "INFO";
//         case Level::WARNING:
//             return "WARNING";
//         case Level::ERROR:
//             return "ERROR";
//         case Level::FATAL:
//             return "FATAL";
//         default:
//             return "UNKONW";
//         }
//     }
//     // 日志内容
//     class Log
//     {
//     public:
//         Log()
//         {
//             _logflush = std::make_unique<displayflush>();
//         }
//         void EnableDisplay()
//         {
//             _logflush = std::make_unique<displayflush>();
//         }
//         void EnableFile()
//         {
//             _logflush = std::make_unique<fileflush>();
//         }
//         // 一条日志信息
//         class Logmassage
//         {
//         public:
//             Logmassage(Level level, std::string filename, int line, Log *log)
//                 : _time(GetTime()),
//                   _level(level),
//                   _pid(getpid()),
//                   _filename(filename),
//                   _line(line),
//                   _log(log)
//             {
//                 std::stringstream ss;
//                 ss << '[' << _time << "] "
//                    << '[' << GetLevel(_level) << "] "
//                    << '[' << _pid << "] "
//                    << '[' << _filename << "] "
//                    << '[' << _line << "] ";
//                 _logmassage = ss.str();
//             }
//             ~Logmassage()
//             {
//                 if (_log->_logflush)
//                 {
//                     // 刷新日志
//                     _log->_logflush->flush(_logmassage);
//                 }
//             }
//             template <class T>
//             Logmassage &operator<<(const T &data)
//             {
//                 std::stringstream ss;
//                 ss << data;
//                 _logmassage += ss.str();
//                 return *this;
//             }

//         private:
//             std::string _time; // 时间
//             Level _level;      // 日志等级
//             pid_t _pid;
//             std::string _filename;   // 文件名
//             int _line;               // 行号
//             std::string _massage;    // 信息
//             std::string _logmassage; // 完整的日志信息
//             Log *_log;               // Log 指针
//         };
//         Logmassage operator()(Level level, std::string filename, int line)
//         {
//             return Logmassage(level, filename, line, this);
//         }

//     private:
//         std::unique_ptr<logflush> _logflush;
//     };
//     Log log;
// #define LOG(level) log(level, __FILE__, __LINE__)
// #define ENABLEDISPLAY() log.EnableDisplay()
// #define ENABLEFILE() log.EnableFile()
// }